<?php

namespace app\common\service\module_manage\netfilter;


use app\adminapiv3\model\AssestIntranetIpModel;
use app\adminapiv3\model\learn_data\HostLearnDataModel;
use app\common\exception\CustomException;
use app\common\model\HostRuleModel;
use app\common\traits\BaseServiceTrait;
use app\common\model\Ip;
use think\Db;
use think\exception\ValidateException;
use think\facade\Config;

class HostLearnData
{
    use BaseServiceTrait;
    
    /**
     * @name: 列表
     * @Param: tcp=>1,udp=>2
     * @Description: 
     * @Author: foo
     * @Date: 2019-11-01 16:31:36
     */
    public function list ($filterData)
    {
        //必须字段 host_id
        if(!isset($filterData['host_id']) || !$filterData['host_id']){
            $this->setError($this->CODE_FAIL,'缺少参数host_id');
            return false;
        }
        //查询回调状态
        $isCallback = Db::name('host_learn_config')->where('host_id',$filterData['host_id'])->value('callback_status');
        if($isCallback === null){
            $this->setError($this->CODE_FAIL,'主机配置不存在');
            return false;
        }

        //图标字段转换  icon=1  => is_add = 1  icon= 2  => is_modify = 1 icon = 3 => is_delete = 1
        if(isset($filterData['icon'])){
            switch ($filterData['icon']){
                case 1:
                    $filterData['is_add'] = 1;
                    $filterData['is_modify'] = 0;
                    $filterData['is_delete'] = 0;
                    break;
                case 2:
                    $filterData['is_add'] = 0;
                    $filterData['is_modify'] = 1;
                    $filterData['is_delete'] = 0;
                    break;
                case 3:
                    $filterData['is_add'] = 0;
                    $filterData['is_modify'] = 0;
                    $filterData['is_delete'] = 1;
                    break;
            }

            unset($filterData['icon']);
        }
        //查询子库
        $learnDataModel = new HostLearnDataModel();
        $learnDataModel->setHostId($filterData['host_id']);

        //查询字段
        $field = 'l.id,l.host_id,l.protocols,l.src_ip,l.dest_ip,l.src_mac,l.dest_mac,l.direction,l.view,l.status,l.have_mac_addr,l.ctime,l.dest_port';
        //如果没有回调,则默认图标数据为0,不连表
        if(!$isCallback){
            $server = $learnDataModel->alias('l')
                ->field($field);

        }else{
            //连表查询
            $field .= ',i.is_add,i.is_modify,i.is_delete';
            $server = $learnDataModel->alias('l')
                ->leftJoin("host_learn_icon i","i.learn_id=l.id AND i.`host_id` = {$filterData['host_id']}")
                ->field($field);
        }

        //筛选
        if(isset($filterData['mark']) && $filterData['mark']){
            $server = $server->where('l.mark', $filterData['mark']);
        }

        isset($filterData['dest_ip']) && !empty($filterData['dest_ip']) ? $server->whereLike('l.dest_ip', '%' . $filterData['dest_ip'] . '%') : '';
        isset($filterData['src_ip']) && !empty($filterData['src_ip']) ? $server->whereLike('l.src_ip', '%' . $filterData['src_ip'] . '%') : '';
        isset($filterData['direction']) && is_numeric($filterData['direction']) ? $server->where('l.direction', $filterData['direction']) : '';
        isset($filterData['dest_port']) && is_numeric($filterData['dest_port']) ? $server->where('l.dest_port', $filterData['dest_port']) : '';
        isset($filterData['protocols']) && is_numeric($filterData['protocols']) ? $server->where('l.protocols', $filterData['protocols']) : '';
        isset($filterData['status']) && is_numeric($filterData['status']) ? $server->where('l.status', $filterData['status']) : '';
        isset($filterData['ctime']) && $filterData['ctime'] > 0 ? $server->where('l.ctime', '>=', $filterData['ctime'])->where('l.ctime', '<=', $filterData['ctime'] + 86400) : '';

        //前端必须是三选一
        //特殊筛选 ： 回调后,筛选图标  is_add is_modify is_delete  没有回调 则 图标数据为空 在没有回调的情况下 如果他选 is_add = 0 我们就该无视筛选规则 选出所有数据
        //但是如果is_add=1 则应该数据因该为空
        if($isCallback){
            isset($filterData['is_add']) && is_numeric($filterData['is_add']) ? $server->where('i.is_add',$filterData['is_add']) : '';
            isset($filterData['is_modify']) && is_numeric($filterData['is_modify']) ? $server->where('i.is_modify',$filterData['is_modify']) : '';
            isset($filterData['is_delete']) && is_numeric($filterData['is_delete']) ? $server->where('i.is_delete',$filterData['is_delete']) : '';
        }else{
            //如果没有回调且用户想筛选图标为1的 则数据为空
            if((isset($filterData['is_add']) &&  $filterData['is_add']) || (isset($filterData['is_modify']) &&  $filterData['is_modify']) || (isset($filterData['is_delete']) &&  $filterData['is_delete'])) {
                return [
                        'total' => 0,
                        'per_page' => Config::get('paginate.list_rows'),
                        'current_page' => 1,
                        'last_page' => 1,
                        'data' => [],
                    ];
            }
        }

        $return = $server->order('l.id','desc')->paginate()->toArray();

        //ip数组
        $ips =  array_unique(array_merge(array_column($return['data'],'src_ip'),array_column($return['data'],'dest_ip')));
        $ips = array_map('ip2long',$ips);

        //查询内网IP表
        $ipModel = new AssestIntranetIpModel();
        $ipsData =  $ipModel->getIpsAndAddress($ips);

        //默认物理地址
        $addressR = './static/IP/qqzeng.dat';
        if(!file_exists($addressR))
        {
            $this->setError($this->CODE_FAIL,'缺少IP文件');
            return false;
        }
        $ipServer = new Ip($addressR);

        foreach ($return['data'] as $key => $item){
            //匹配图标,如果没有回调 icon = 0 全部没有图标
            if(!$isCallback){
                $return['data'][$key]['is_add'] = 0;
                $return['data'][$key]['is_modify'] = 0;
                $return['data'][$key]['is_delete'] = 0;
            }else{
                //如果回调了,则默认值为0
                if(!$item['is_add']){
                    $return['data'][$key]['is_add'] = 0;
                }
                if(!$item['is_modify']){
                    $return['data'][$key]['is_modify'] = 0;
                }
                if(!$item['is_delete']){
                    $return['data'][$key]['is_delete'] = 0;
                }
            }

            //根据IP文件,查询ip对应的地址address作为默认值
            //源IP物理地址
            $ipInfo = $ipServer->getlocation($item['src_ip']);
            if($ipInfo){
                $return['data'][$key]['src_ip_address'] = $ipInfo['country'].$ipInfo['province'].$ipInfo['city'].$ipInfo['area'];
            }else{
                $return['data'][$key]['src_ip_address'] = '';
            }

            //目标IP物理地址
            $ipInfo = $ipServer->getlocation($item['dest_ip']);
            if($ipInfo){
                $return['data'][$key]['dest_ip_address'] = $ipInfo['country'].$ipInfo['province'].$ipInfo['city'].$ipInfo['area'];
            }else{
                $return['data'][$key]['dest_ip_address'] = '';
            }

            //匹配IP 包括源IP 目标IP 默认值(外网)：extranet
            $item['src_ip'] = ip2long($item['src_ip']);
            $item['dest_ip'] = ip2long($item['dest_ip']);

            $return['data'][$key]['src_ip_type'] = 'extranet';
            $return['data'][$key]['dest_ip_type'] = 'extranet';

            foreach ($ipsData as $row){
                //匹配源IP
                if($row['ip_s_int'] <= $item['src_ip'] && $row['ip_e_int'] >= $item['src_ip']){
                    $return['data'][$key]['src_ip_type'] = $row['type'];
                    //源IP物理地址
                    $return['data'][$key]['src_ip_address'] = $row['address'];
                }
                //匹配目标IP
                if($row['ip_s_int'] <= $item['dest_ip'] && $row['ip_e_int'] >= $item['dest_ip']){
                    $return['data'][$key]['dest_ip_type'] = $row['type'];
                    //目标IP物理地址
                    $return['data'][$key]['dest_ip_address'] = $row['address'];
                }
            }
        }
        return $return;
    }

    /**
     * @name: 组列表
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-11-04 15:01:38
     */
    public function  groupList ($filterData)
    {
        if(!isset($filterData['host_id']) || !$filterData['host_id']){
            throw new ValidateException('缺少必须参数host_id',201);
        }

        //查询子库数据
        $learnDataModel = new HostLearnDataModel();
        $learnDataModel->setHostId($filterData['host_id']);

        $server = $learnDataModel->field('host_id,src_ip,dest_ip,mark')->where('host_id', $filterData['host_id']);

        isset($filterData['dest_ip']) && !empty($filterData['dest_ip']) ? $server->whereLike('dest_ip', '%' . $filterData['dest_ip'] . '%') : '';
        isset($filterData['src_ip']) && !empty($filterData['src_ip']) ? $server->whereLike('src_ip', '%' . $filterData['src_ip'] . '%') : '';

        $return = $server->group('mark')->paginate()->toArray();

        //ip数组
        $ips =  array_unique(array_merge(array_column($return['data'],'src_ip'),array_column($return['data'],'dest_ip')));
        $ips = array_map('ip2long',$ips);

        //查询内网IP表
        $ipModel = new AssestIntranetIpModel();
        $ipsData =  $ipModel->getIpsAndAddress($ips);
        //默认物理地址
        $addressR = './static/IP/qqzeng.dat';
        if(!file_exists($addressR))
        {
            $this->setError($this->CODE_FAIL,'缺少IP文件');
            return false;
        }
        $ipServer = new Ip($addressR);
        //匹配IP
        foreach ($return['data'] as $key => $item){
            //根据IP文件,查询ip对应的地址address作为默认值
            //源IP物理地址
            $ipInfo = $ipServer->getlocation($item['src_ip']);
            if($ipInfo){
                $return['data'][$key]['src_ip_address'] = $ipInfo['country'].$ipInfo['province'].$ipInfo['city'].$ipInfo['area'];
            }else{
                $return['data'][$key]['src_ip_address'] = '';
            }

            //目标IP物理地址
            $ipInfo = $ipServer->getlocation($item['dest_ip']);
            if($ipInfo){
                $return['data'][$key]['dest_ip_address'] = $ipInfo['country'].$ipInfo['province'].$ipInfo['city'].$ipInfo['area'];
            }else{
                $return['data'][$key]['dest_ip_address'] = '';
            }

            //匹配IP 包括源IP 目标IP 默认值(外网)：extranet
            $item['src_ip'] = ip2long($item['src_ip']);
            $item['dest_ip'] = ip2long($item['dest_ip']);

            $return['data'][$key]['src_ip_type'] = 'extranet';
            $return['data'][$key]['dest_ip_type'] = 'extranet';

            foreach ($ipsData as $row){
                //匹配源IP
                if($row['ip_s_int'] <= $item['src_ip'] && $row['ip_e_int'] >= $item['src_ip']){
                    $return['data'][$key]['src_ip_type'] = $row['type'];
                    //源IP物理地址
                    $return['data'][$key]['src_ip_address'] = $row['address'];
                }
                //匹配目标IP
                if($row['ip_s_int'] <= $item['dest_ip'] && $row['ip_e_int'] >= $item['dest_ip']){
                    $return['data'][$key]['dest_ip_type'] = $row['type'];
                    //目标IP物理地址
                    $return['data'][$key]['dest_ip_address'] = $row['address'];
                }
            }
        }
        return $return;
    }

    /**
     * @name: 统计列表
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-11-04 15:07:09
     */
    public function statisticsList ($host_id, $mark, $type = '')
    {
        //查询子库
        $learnDataModel = new HostLearnDataModel();
        $learnDataModel->setHostId($host_id);

        $server = $learnDataModel->whereIn('mark', $mark);

        $type == 'all_view' ? $server->field('mark,SUM(view) as ' . $type) : $server->field('mark,count(*) as ' . $type);
        $type == 'outbount' ? $server->where('direction', 1): '' ;
        $type == 'inbount' ? $server->where('direction', 2): '' ;
        $type == 'have_rule' ? $server->where('status', 1): '' ;

        $data = $server->group('mark')->select()->toArray();
        return array_column($data, $type, 'mark');
    }

    /**
     * @name: 删除
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-11-04 14:17:27
     */
    public function delete ($host_id, $learn_id)
    {
        $hostLearnDataModel = new HostLearnDataModel();
        $hostLearnDataModel->setHostId($host_id);
        $hostLearnDataModel->where('host_id', $host_id)->whereIn('id', $learn_id)->delete();
    }

    /**
     * @name: 创建规则
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-11-04 16:16:43
     */
    public function createRule ($host_id, $learn_id)
    {
        $os = $this->getHost('os', 'string');
        $config = Db::name('host_learn_config')->where('host_id', $host_id)->find();
        //查询子库
        $learnDataModel = new HostLearnDataModel();
        $learnDataModel->setHostId($host_id);

        //修改状态
        $learnDataModel->whereIn('id', $learn_id)->update(['status' => 1]);
        $learn_id_s = implode(',', $learn_id);

        $ruleModel = new HostRuleModel();
        $ruleModel->insertFromLearnData($host_id, $learn_id_s, $config['allow'], $config['have_mac_addr'], $os);//插入数据
    }

    /**
     * @name: 通过学习id和主机id查询
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-12-16 18:14:56
     */
    public function selectByLearnId ($host_id, $learn_id)
    {
        //查询子库
        $learnDataModel = new HostLearnDataModel();
        $learnDataModel->setHostId($host_id);

        $learnData = $learnDataModel->alias('hld')
            ->whereIn('hld.id', $learn_id)
            ->field('hld.*')
            ->select()->toArray();

        //查询配置表
        $config = Db::name('host_learn_config')->field('allow')->where('host_id',$host_id)->findOrEmpty();
        //匹配数据
        foreach ($learnData as $key => $item){
            $learnData[$key]['allow'] = $config['allow'];
        }

        if(empty($learnData))
        {
           throw new CustomException('不存在的学习数据', 422);
        }
        
        return $learnData;
    }

    /**
     * @name: 发送到客户端格式
     * @Param: 
     * @Description: 
     * @Author: foo
     * @Date: 2019-12-16 18:19:21
     */
    public function clientRule ($rule)
    {
        $return = [
            'alias' => '' ,
            'direction' => $rule['direction'],
            'protocols' => $rule['protocols'],
            'process' => $rule['allow'],
            'dest_port' => $rule['dest_port'],
            'src_port' => 'any',
            'src_ip' => $rule['src_ip'],
            'dest_ip' => $rule['dest_ip'],
            'mac_addr' => $rule['direction'] == 1 ? $rule['dest_mac'] : $rule['src_mac'],
            'have_mac_addr' => $rule['have_mac_addr'],
        ];

        return $return;
    }
}